
## Snakes on a Payload
## Copyright (C) 2011 CORE Security Technologies
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
##
## Authors:
## 2011/09/11 - Pedro Varangot (pvarangot@)
## 2011/09/11 - Fernando Russ (fruss@)



import sys

import tempfile
from objcopy import ObjCopy
from elfsource import ReadElfSource
import struct
import os
import argparse
from relocation import Relocation
import itertools
from archs import Arch


layout = (".prelude",
          ".text",
          ".plt",
          ".got",
          ".got.plt",
          ".rodata",
          ".data",
          ".data.rel.ro",
          ".bss",
          ".epilog",
          ".rebase",
          ".endoftheworld")

#
# cmd-line args parsing
#
parser = argparse.ArgumentParser(description='s2l - Snakes on a Payload - Copyright (C) 2011 CORE Security Technologies', epilog="relax.")
parser.add_argument('IN_FILE', help='the .so to process.')
parser.add_argument('OUT_FILE', help='the output file')
parser.add_argument('-l', dest='INPUT_LIBS', default=list(), action='append', help='add library to pseudo-link to.(eg: -l/lib/x86_64-linux-gnu/libm.so.6)')
parser.add_argument('-a', dest='TARGET_ARCH', default="no-arch!", help='target arch (eg: android-arm, linux-i386)')

args = parser.parse_args()
arch = Arch.byName( args.TARGET_ARCH )
elf = ReadElfSource(arch, args.IN_FILE )
blob = ObjCopy(arch, layout, args.IN_FILE )
input_libs = [ arch.libs.byName( libname ) for libname in set(args.INPUT_LIBS) ]


# apply relocations
for relocation in elf.relocations:
    relocation.apply( blob, None)
#
# emit stage
#
with open(args.OUT_FILE, "w+b") as _f:
    _f.write( blob ) 
    
    # match _JUMP_SLO symbols against the dynamic libs.
    jump_slots = [relocation for relocation in elf.relocations if "_JUMP_SLO" in relocation.NAME]    
        
    resolved_relocations = []
    resolved_symbols = set()
    externals_symbols = set( [reloc.symbol.name for reloc in jump_slots] )        
    undefined_symbols = set([symbol.name for symbol in elf.symbols.values() if symbol.ndx == 'UND'])    
    for lib in input_libs:    
        resolved_relocation = [reloc for reloc in jump_slots if reloc.symbol.name in lib.symbols]
        if len(resolved_relocation) > 0:
            resolved_relocations.append( (lib, resolved_relocation) )             
            resolved_symbols.update( [reloc.symbol.name for reloc in resolved_relocation] )
        
    
    unresolved_symbols = undefined_symbols - resolved_symbols
    
    for unresolved_symbol in unresolved_symbols:
        print "WARNING: unresolved symbol '%s'" % (unresolved_symbol)
    
    
    for lib, relocations in resolved_relocations:
        # load the library        
        Relocation.emitEntry( _f, arch, type = Relocation.RelocType.LOADLIB, name = lib.exportAs)
        # load symbols for this lib
        for relocs in relocations:
            Relocation.emitEntry( _f, arch, type = Relocation.RelocType.JMPSLOT,
                                      offset = relocs.offset,
                                      name = relocs.symbol.name)
                                              
    # emit rebase relocation
    for relocation in (relocation for relocation in elf.relocations if relocation.symbol.name not in resolved_symbols):
        Relocation.emitEntry(_f, arch, type = Relocation.RelocType.REBASE, 
                                 offset = relocation.offset )

    # no more relocation commands
    Relocation.emitEntry(_f, arch, type = Relocation.RelocType.END )
